#include <dspSimulator/instruction/stnw.h>

bool STNW::checkCondition(std::shared_ptr<DspMachine> dspMachine) {
    if (isCompactInstruction) {
        return true;
    } else {
        return checkStandardCondition(dspMachine);
    }
}

void STNW::loadInstIRIntoJit(llvm::LLVMContext *llvmContext, llvm::Module *module,
                             std::shared_ptr<llvm::IRBuilder<>> irBuilder) {

}

std::shared_ptr<Instruction> STNW::decode(std::shared_ptr<InstInfo> instInfo, u64 baseCycle) {
    std::shared_ptr<STNW> stnw;
    auto dspInstInfo = std::dynamic_pointer_cast<DspInstInfo>(instInfo);
    bool isStandardInst = dspInstInfo->isStandardInstruction();
    if (isStandardInst) {
        auto data = vectorToBitSet<32>(dspInstInfo->getBits());
        if (checkBits<5>(data, 2, 0b10101) && checkBits<1>(data, 8, 0b1) && existStandardCondition(data)) {
            stnw = std::make_shared<STNW>(dspInstInfo->getAddress(), baseCycle);
            stnw->creg_z = extractBits<4>(data, 28);
            stnw->y = data[7];
            if(stnw->y == 0){
                stnw->funcUnit = FuncUnit::D1;
            }else{
                stnw->funcUnit = FuncUnit::D2;
            }
            stnw->mode = extractBits<4>(data, 9).to_ulong();
            stnw->offsetR = extractBits<5>(data, 13).to_ulong();
            stnw->cst = stnw->offsetR;
            stnw->baseR = extractBits<5>(data, 18).to_ulong();
            stnw->src1 = extractBits<5>(data, 23).to_ulong();
            stnw->s = data[1];
            stnw->p = data[0];
            stnw->instFormat = InstFormat::Kind1;
        }
    }
    return stnw;
}

void STNW::executeCustom(std::shared_ptr<TargetMachine> targetMachine, ExecutePhase executePhase) {
    auto dspMachine = std::dynamic_pointer_cast<DspMachine>(targetMachine);
    switch (executePhase) {
        case ExecutePhase::E1:
            if (instFormat == InstFormat::Kind1) {
                u32 *baseRReadPtr;
                u32 *baseRWritePtr;
                if (y == 0) {
                    baseRReadPtr = dspMachine->getSaveRegs()->getReg32ByName(RegAName[baseR])->getDataPtr();
                    baseRWritePtr = dspMachine->getRegisters()->getReg32ByName(RegAName[baseR])->getDataPtr();
                } else {
                    baseRReadPtr = dspMachine->getSaveRegs()->getReg32ByName(RegBName[baseR])->getDataPtr();
                    baseRWritePtr = dspMachine->getRegisters()->getReg32ByName(RegBName[baseR])->getDataPtr();
                }
                m_visitMemAddr = computeRealAddress(baseRReadPtr, baseRWritePtr, mode, 2, dspMachine);
            }
            m_data = *src1LoRegPtr;
            break;
        case ExecutePhase::E3:
            targetMachine->getMMU()->getMemory()->writeNBits<32>(m_visitMemAddr, {m_data});
            break;
        default:
            break;
    }
}

std::string STNW::toString() const {
    std::string res;
    if (!isCompactInstruction) {
        res += getStandardConditionString() + " ";
    }
    res += m_name + " " + FuncUnitName[static_cast<u32>(funcUnit)];
    switch (instFormat) {
        case InstFormat::Kind1:
            res += " " + getSrc1Reg32Name() + "," + addressModeToString();
            break;
        default:
            break;
    }
    return res;
}

std::shared_ptr<DspInstruction> STNW::clone() {
    return std::make_shared<STNW>(*this);
}
